package service

import (
	"api/app/ctl/auto/v1/mc"
	"api/app/ctl/models"
	"api/config"
	"api/libs/errs"
	"api/libs/orm"
	"context"
	"errors"
	"fmt"
	"gitee.com/dreamwood/ez"
	"math/rand"
	"net/http"
	"net/http/httputil"
	"sync"
	"time"
)

// GateWays 网关全局变量，键为监听的端口,值为此端口下启动的http.Server
var GateWays map[string]*http.Server

// Hosts 主机全局变量 键为主机的AppId,值为此ID下的所有负载主机
var Hosts map[string][]*models.AppHost

// Routes 路由全局变量
var Routes []*models.AppRoute

var mt sync.Mutex

func InitGateWay() {
	GateWays = make(map[string]*http.Server)
	Hosts = make(map[string][]*models.AppHost)
	Routes = make([]*models.AppRoute, 0)
	mt = sync.Mutex{}
	ez.Debug("开始准备GateWay主机数据")
	PrepareHost()
	ez.Debug("开始准备GateWay路由转发数据")
	PrepareRoute()
	ez.Debug("开始启动端口监听")
	AutoStartGateWay()
}

func GetLock() *sync.Mutex {
	return &mt
}

func PrepareHost() {
	f := orm.NewFactory(mc.AppHostEzConfig)
	hosts := make([]*models.AppHost, 0)
	e := f.FindBy(&hosts, ez.M{"isOn": true, "state": 1}, nil, 0, 0)
	if errs.Try(e) {
		return
	}
	//对HostsMap操作加锁
	mt.Lock()
	defer mt.Unlock()
	//先清空现有的数据
	Hosts = make(map[string][]*models.AppHost)
	for _, host := range hosts {
		find, ok := Hosts[host.AppId]
		if !ok {
			find = make([]*models.AppHost, 0)
		}
		find = append(find, host)
		Hosts[host.AppId] = find
	}
}

func PrepareRoute() {
	f := orm.NewFactory(mc.AppRouteEzConfig)
	routes := make([]*models.AppRoute, 0)
	e := f.FindBy(&routes, ez.M{"isOn": true}, []string{"appId"}, 0, 0)
	if errs.Try(e) {
		return
	}
	mt.Lock()
	defer mt.Unlock()
	Routes = make([]*models.AppRoute, 0)
	for _, route := range routes {
		Routes = append(Routes, route)
	}
}

func CreateDirector(port string) func(*http.Request) {
	var Director = func(request *http.Request) {
		urlRedirect := ""
		maxSort := -1
		maxSortAppId := ""
		for _, route := range Routes {
			if route.Port != port {
				continue
			}
			pattern := ez.ReplaceAll(route.From, `:(\w+)\((.+?)\)`, "(?P<$1>$2)")
			matches := ez.MatchAll(request.URL.Path, pattern)
			if len(matches) > 0 {
				if route.Sort > maxSort {
					urlRedirect = ez.ReplaceAll(request.URL.Path, pattern, route.To)
					maxSort = route.Sort
					maxSortAppId = route.AppId
				}
			}
		}
		//取出所有负载host
		hosts, ok := Hosts[maxSortAppId]
		if !ok {
			//没有相应的主机
			errs.Create("主机未找到")
			serverConf := config.GetCore()
			request.URL.Path = "/_server_404"
			request.URL.RawQuery = "code=NO_HOST"
			request.URL.Host = fmt.Sprintf("%s:%d", serverConf.ServerHost, serverConf.ServerPort)
			request.URL.Scheme = "http"
			return
		}
		appHosts := make([]*models.AppHost, 0)
		for _, appHost := range hosts {
			if appHost.IsOn {
				appHosts = append(appHosts, appHost)
			}
		}
		//找到了匹配的应用
		targetHost := "127.0.0.1"
		if len(appHosts) == 1 {
			targetHost = fmt.Sprintf("%s:%s", appHosts[0].Host, appHosts[0].Port)
		} else if len(appHosts) > 1 {
			rand.Seed(time.Now().UnixNano())
			rnd := rand.Intn(len(appHosts))
			targetHost = fmt.Sprintf("%s:%s", appHosts[rnd].Host, appHosts[rnd].Port)
		} else {
			errs.Create("主机全部掉线")
			serverConf := config.GetCore()
			request.URL.Path = "/_server_500"
			request.URL.RawQuery = "code=NO_HOST"
			request.URL.Host = fmt.Sprintf("%s:%d", serverConf.ServerHost, serverConf.ServerPort)
			request.URL.Scheme = "http"
		}
		if targetHost[len(targetHost)-3:] == ":80" {
			targetHost = targetHost[:len(targetHost)-3]
		}
		println(targetHost, urlRedirect)
		request.URL.Host = targetHost
		request.URL.Path = urlRedirect

		request.URL.Scheme = "http"
	}
	return Director
}

func Responsor(response *http.Response) error {
	if response.Request.Method == "OPTIONS" {
		return nil
	}
	//检查是否需要更新
	response.Header.Set("Access-Control-Allow-Origin", "*")
	return nil
}

// func StartGateWay(gt *models.GateWay) {
func StartGateWay(gateway *models.GateWay) {
	port := gateway.Port
	proxyConf := httputil.ReverseProxy{
		Director:       CreateDirector(port),
		ModifyResponse: Responsor,
	}
	ez.Debug(fmt.Sprintf("路由转发中心配置完成,监听端口:%s", port))

	existedGtw, ok := GateWays[port]
	if ok {
		e := existedGtw.Shutdown(context.TODO())
		ez.PrintError(e)
	}
	srv := &http.Server{
		Addr:    fmt.Sprintf("%s:%s", gateway.Host, port),
		Handler: &proxyConf,
	}
	GateWays[port] = srv
	err := srv.ListenAndServe()
	if err != nil {
		ez.Debug(fmt.Sprintf("路由转发中心端口监听时发生错误:%s", err.Error()))
	}
}

func StopGateWay(port string) (err error) {
	find, ok := GateWays[port]
	if !ok {
		err = errors.New("网关已不存在")
		return
	}
	err = find.Shutdown(context.TODO())
	delete(GateWays, port)
	return
}

func AutoStartGateWay() {
	f := orm.NewFactory(mc.GateWayEzConfig)
	gateWays := make([]*models.GateWay, 0)
	e := f.FindBy(&gateWays, ez.M{"isOn": true}, nil, 0, 0)
	ez.PrintError(e)
	for _, gateway := range gateWays {
		if gateway.IsOn {
			go StartGateWay(gateway)
		}
	}
}
